#ifndef __tenacitas_log__file_controler__h__
#define __tenacitas_log__file_controler__h__


/** @file
    In this file is defined class change_file_handler */

/** todo */

// ==> C++ Headers
#include <fstream>
#include <string>
#include <cstdint>

// ==> 3rds Headers
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>

// ==> Our Headers
#include <tenacitas.exception/exception.h>
#include <tenacitas.boost_tools/utils.h>

// ==> Namespaces
using namespace std;

// ==> Macro Contants

// ==> Macro Commands


/** namespace of the organization */
namespace tenacitas {
    /** namespace for the project */
    namespace log {

        // ==> Typedefs 

        // ==> Pre-Declarations

        // ==> Attributes 

        // ==> Functions
        
        // ==> Classes

        /** This class defines how to handle the event of a log file change

            The tenacitas::log::file template class requires a class that handles
            the situation when the log file needs to be changed.

            The @p config method allows one to define the directory where the log
            files will be generated, the name of the program and the processs id
            of the process that is generating the log messages.

            Based on that, the @p file_name method generates a name for the log
            file with this format:

            <tt><i>pgm-name</i>_<i>pid</i>_<i>thread-id</i>_<i>file-index</i>.log</tt>

            The @p thread-id is the thread where the log messages are being
            generated, and @p file-index is a sequential number to tell one log
            file of the <tt>pgm-name, pid, thread-id</tt> from the other.

            When an instance of tenacitas::log::file calls @p file_changed or @p
            file_closed, the log file is renamed to:

            <tt><i>pgm-name</i>_<i>pid</i>_<i>thread-id</i>_<i>file-index</i>.log.END</tt>

            For example, if the log files should be created in directory
            <tt>/var/tmp/</tt>, and a log files is created for a program called
            @p hash, in process @p 3216, in thread @p 7854, we would have a file called: 
            <tt>
            /var/tmp/hash_3216_7854_0000000000.log
            </tt>

            Under the event of a log file change, we would have the files:
            <tt>
            /var/tmp/hash_3216_7854_0000000000.log.END
            /var/tmp/hash_3216_7854_0000000001.log
            </tt>

            When thread @p 7854 ends, we would have:
            <tt>
            /var/tmp/hash_3216_7854_0000000000.log.END
            /var/tmp/hash_3216_7854_0000000001.log.END
            </tt>
            

            <b>A word to the programmer user</b>
            You should call static method @p config to define the directory where
            the log files will be created, the program name of the program that
            will generate the log messages, along with the process id.
            That is it! The @p tenacitas::log::file class that will use this
            @p change_file_hander class to call the other necessary methods
            @nosubgrouping 
        */
        class change_file_handler {

        public:

            //  ==> change_file_handler Public Internal Types

            /** Type of pointer to */
            typedef boost::shared_ptr<change_file_handler> ptr;

            //  ==> change_file_handler Public Constructors

            /** Cofigures the factory

                @param  p_dir_log is the directory where the log files will be
                created
                @param  p_pgm_name is the name of the program name that will
                generate the log messages
                @param  p_pid is the process identifier of the process that is
                generating the log messages
            */
            inline static void config( const std::string & p_dir_log,
                                       const std::string & p_pgm_name,
                                       int64_t p_pid );
            
            /** Destructor */
            ~change_file_handler( ) {}

            /** Creates a pointer

                @param  p_thread_id is the identifier of the thread for which log
                messages will be loged in this file 
            */
            inline static ptr create( boost::thread::id p_thread_id );

            /** not allowed */
            change_file_handler( const change_file_handler & ) = delete; 

            /** not allowed */
            change_file_handler( change_file_handler && ) = delete;

            //  ==> change_file_handler Public Accessors 

            /** Retrives the file name for the log file */
            inline const std::string & file_name( );

            //  ==> change_file_handler Public Processors

            /** Informs that the log file should be changed */
            inline void file_changed( );

            /** Informs that the log file should be closed */
            inline void file_closed( );

            //  ==> change_file_policy - Public Operators

            /** not allowed */
            change_file_handler & operator=( const change_file_handler & ) =
                delete; 

            /** not allowed */
            change_file_handler & operator=( change_file_handler && ) =
                delete;

        private:

            //  ==> change_file_handler Private Constructors 

            /** Constructor

                @param  p_thread_id is the identifier of the thread for which log
                messages will be loged in this file 
            */
            inline change_file_handler( boost::thread::id p_thread_id );

            //  ==> change_file_handler Private Processors 

            /** Assembles a new log file name

             @param p_file_index is the sequential number that tells one log file
             of a ( program, pid, thread) from another */
            const std::string & file_name( int32_t p_file_index );

            /** Renames the log file */
            inline void rename_file( );

            //  ==> change_file_handler Private Attributes 

            /** thread identifier that will log messages */
            boost::thread::id m_thread_id;

            /** index of the log file, where 0 is the first */
            int32_t m_file_index;

            /** name of the currente log file */
            std::string m_current_file_name;

            /** log directory where the log files will be created */
            static std::string m_dir_log;

            /** name of the program name that will generate the log messages */
            static std::string m_pgm_name;

            /** process identifier of the process that is generating the log
                messages */
            static int64_t m_pid;

        };
    }
}

// =============================================================================
// I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::log;

// -----------------------------------------------------------------------------
const std::string &
change_file_handler::
file_name( ) {
    return file_name( ++m_file_index );
}

// -----------------------------------------------------------------------------            
void
change_file_handler::
file_closed( ) {rename_file( );}

// -----------------------------------------------------------------------------            
void
change_file_handler::
file_changed( ) {rename_file( );}

// -----------------------------------------------------------------------------
change_file_handler::
change_file_handler( boost::thread::id p_thread_id )
    : m_thread_id( p_thread_id ),
      m_file_index( -1 ),
      m_current_file_name( ){}

// -----------------------------------------------------------------------------
change_file_handler::ptr
change_file_handler::
create( boost::thread::id p_thread_id ) {
    if ( m_pid == -1 ) {
        tenacitas::exception::exception l_ex =
            make_tenacitas_exception ("Process ID not configured for "
                                      "change_file_policy ");
        throw l_ex;
    }
    return ptr( new change_file_handler( p_thread_id ) );
}



// -----------------------------------------------------------------------------
void
change_file_handler::
config( const std::string & p_dir_log,
        const std::string & p_pgm_name,
        int64_t p_pid ) {
    m_dir_log = std::string( p_dir_log + "/" );
    m_pgm_name = p_pgm_name;
    m_pid = p_pid;
}

// -----------------------------------------------------------------------------            
void
change_file_handler::
rename_file( ) {
    const std::string l_log_file_name_ended ( m_current_file_name +
                                              std::string( ".END" ) );

    std::rename( m_current_file_name.c_str( ),
                 l_log_file_name_ended.c_str( ) );
}


#endif
